﻿namespace Contoso.ActiveDirectory {

    #region Usings
    using System;
    using System.Collections.Generic;
    using System.DirectoryServices;
    using System.Linq;
    using System.Security.AccessControl;
    using System.Security.Principal;
    using System.Text;
    using System.Threading.Tasks;
    #endregion

    [Serializable]
    public class ExchangeBaseAttributes : IExchangeBaseAttributes {

        public static readonly string[] AttributesToGet;

        #region IExchangeBaseAttributes properties
        public string AltRecipient { get; private set; }

        public string LegacyExchangeDN { get; private set; }

        public string MailNickname { get; private set; }

        public string MapiRecipient { get; private set; }

        public int MSExchALObjectVersion { get; private set; }

        public bool MSExchHideFromAddressLists { get; private set; }

        public IReadOnlyList<ActiveDirectoryAccessRule> MSExchMailboxExplicitAccessRules {
            get {
                if ((msExchMailboxExplicitAccessRules == null) || (msExchMailboxExplicitAccessRules.Count == 0)) {
                    msExchMailboxExplicitAccessRules = new List<ActiveDirectoryAccessRule>();
                    if (this.MSExchMailboxSecurityDescriptor != null) {
                        foreach (ActiveDirectoryAccessRule accessRule in 
                            this.MSExchMailboxSecurityDescriptor.GetAccessRules(includeExplicit: true, includeInherited: false, targetType: typeof(SecurityIdentifier))) {
                            if (ADDataAccess.UnknownSIDs.ContainsKey(accessRule.IdentityReference.Value)) continue;

                            string account = "UNKNOWN";
                            try {
                                account = accessRule.IdentityReference.Translate(typeof(NTAccount)).Value;
                                msExchMailboxExplicitAccessRules.Add(accessRule);
                            }
                            catch (IdentityNotMappedException) {
                                lock (ADDataAccess.UnknownSIDs) {
                                    if (!ADDataAccess.UnknownSIDs.ContainsKey(accessRule.IdentityReference.Value)) {
                                        ADDataAccess.UnknownSIDs.Add(accessRule.IdentityReference.Value, accessRule.IdentityReference as SecurityIdentifier);
                                    }
                                }
                                continue;
                            }
                            catch (SystemException e) {
                                if (e.Message.IndexOf("The trust relationship between the primary domain and the trusted domain failed", StringComparison.OrdinalIgnoreCase) > -1) {
                                    lock (ADDataAccess.UnknownSIDs) {
                                        if (!ADDataAccess.UnknownSIDs.ContainsKey(accessRule.IdentityReference.Value)) {
                                            ADDataAccess.UnknownSIDs.Add(accessRule.IdentityReference.Value, accessRule.IdentityReference as SecurityIdentifier);
                                        }
                                    }
                                }
                                else throw;
                            }
                        }
                    }
                }

                return msExchMailboxExplicitAccessRules;
            }
        }
        [NonSerialized]
        private List<ActiveDirectoryAccessRule> msExchMailboxExplicitAccessRules;

        public ActiveDirectorySecurity MSExchMailboxSecurityDescriptor { get; private set; }

        public MSExchRecipientDisplayType MSExchRecipientDisplayType { get { return msExchRecipientDisplayType; } }
        private MSExchRecipientDisplayType msExchRecipientDisplayType;

        public MSExchRecipientTypeDetails MSExchRecipientTypeDetails { get { return msExchRecipientTypeDetails; } }
        private MSExchRecipientTypeDetails msExchRecipientTypeDetails;

        public MSExchVersion MSExchVersion { get { return msExchVersion; } }
        private MSExchVersion msExchVersion;

        /// <summary>
        /// The collection of all ProxyAddresses, SMTP, X400, and X500, with prefixes
        /// </summary>
        public IReadOnlyList<string> ProxyAddresses { get; private set; }

        /// <summary>
        /// ProxyAddresses for SMTP addresses, filtered to remove the SMTP: prefix, with primary address first
        /// </summary>
        public IReadOnlyList<string> ProxyAddressesSMTPFiltered { get; private set; }

        public IReadOnlyList<string> ShowInAddressBook { get; private set; }

        public string TargetAddress { get; private set; }
        #endregion

        #region Constructors
        static ExchangeBaseAttributes() {
            AttributesToGet = new string[]
            {
                "altRecipient",
                "legacyExchangeDN",
                "mailNickname",
                "mapiRecipient",
                "msExchALObjectVersion",
                "msExchHideFromAddressLists",
                "msExchMailboxSecurityDescriptor",
                "msExchRecipientDisplayType",
                "msExchRecipientTypeDetails",
                "msExchVersion",
                "proxyAddresses",
                "showInAddressBook",
                "targetAddress"
            };
        }
        public ExchangeBaseAttributes() {
            this.AltRecipient = string.Empty;
            this.LegacyExchangeDN = string.Empty;
            this.MailNickname = string.Empty;
            this.MapiRecipient = string.Empty;
            msExchMailboxExplicitAccessRules = new List<ActiveDirectoryAccessRule>();
            msExchRecipientDisplayType = MSExchRecipientDisplayType.MailboxUser;
            msExchRecipientTypeDetails = MSExchRecipientTypeDetails.Undefined;
            msExchVersion = MSExchVersion.Undefined;
            this.ProxyAddresses = new List<string>();
            this.ProxyAddressesSMTPFiltered = new List<string>();
            this.ShowInAddressBook = new List<string>();
            this.TargetAddress = string.Empty;            
        } 
        #endregion

        #region Methods
        public void ProcessResults(Dictionary<string, object[]> attributes) {
            if (attributes.ContainsKey("altRecipient")) {
                this.AltRecipient = attributes["altRecipient"][0].ToString();
            }
            if (attributes.ContainsKey("legacyExchangeDN")) {
                this.LegacyExchangeDN = attributes["legacyExchangeDN"][0].ToString();
            }
            if (attributes.ContainsKey("mailNickname")) {
                this.MailNickname = attributes["mailNickname"][0].ToString().ToLower();
            }
            if (attributes.ContainsKey("mapiRecipient")) {
                this.MapiRecipient = attributes["mapiRecipient"][0].ToString().ToLower();
            }
            if (attributes.ContainsKey("msExchALObjectVersion")) {
                this.MSExchALObjectVersion = Convert.ToInt32(attributes["msExchALObjectVersion"][0].ToString());
            }
            if (attributes.ContainsKey("msExchHideFromAddressLists")) {
                this.MSExchHideFromAddressLists = Convert.ToBoolean(attributes["msExchHideFromAddressLists"][0].ToString());
            }
            if (attributes.ContainsKey("msExchMailboxSecurityDescriptor")) {
                this.MSExchMailboxSecurityDescriptor = new ActiveDirectorySecurity();
                if (attributes["msExchMailboxSecurityDescriptor"][0] as byte[] != null) {
                    this.MSExchMailboxSecurityDescriptor.SetSecurityDescriptorBinaryForm(attributes["msExchMailboxSecurityDescriptor"][0] as byte[], AccessControlSections.All);
                }
                else {
                    this.MSExchMailboxSecurityDescriptor.SetSecurityDescriptorBinaryForm(attributes["msExchMailboxSecurityDescriptor"][0].ToString().FromBase64(), AccessControlSections.All);
                }
            }
            if (attributes.ContainsKey("msExchRecipientDisplayType")) {
                Enum.TryParse<MSExchRecipientDisplayType>(
                    attributes["msExchRecipientDisplayType"][0].ToString(), result: out msExchRecipientDisplayType);
            }
            if (attributes.ContainsKey("msExchRecipientTypeDetails")) {
                Enum.TryParse<MSExchRecipientTypeDetails>(
                    attributes["msExchRecipientTypeDetails"][0].ToString(), result: out msExchRecipientTypeDetails);
            }
            if (attributes.ContainsKey("msExchVersion")) {
                Enum.TryParse<MSExchVersion>(
                    attributes["msExchVersion"][0].ToString(), result: out msExchVersion);
            }
            if (attributes.ContainsKey("showInAddressBook")) {
                this.ShowInAddressBook = ADDataAccess.GetMultiValuedAttribute<string>("showInAddressBook", attributes);
            }
            if (attributes.ContainsKey("targetAddress")) {
                this.TargetAddress = attributes["targetAddress"][0].ToString().ToLower().Replace("smtp:", string.Empty);
            }

            #region get the list of ProxyAddresses and SMTP addresses

            if (attributes.ContainsKey("proxyAddresses")) {
                var proxyAddresses = new List<string>();
                for (int index = 0; index < attributes["proxyAddresses"].Length; index++) {
                    string proxyAddress = attributes["proxyAddresses"][index].ToString();
                    if (!string.IsNullOrWhiteSpace(proxyAddress)) {
                        proxyAddresses.Add(proxyAddress);
                    }
                }

                this.ProxyAddresses = new List<string>(proxyAddresses);
            }

            if (this.ProxyAddresses.Count > 0) {
                var proxyAddressesSmtpFiltered = new List<string>();
                foreach (var proxyAddress in this.ProxyAddresses) {
                    if (proxyAddress.StartsWith("smtp:", StringComparison.OrdinalIgnoreCase)) {
                        // the primary SMTP address is uppercase
                        if (proxyAddress.StartsWith("SMTP:")) {
                            proxyAddressesSmtpFiltered.Insert(0, proxyAddress.Substring(@"SMTP:".Length));
                        }
                        else {
                            proxyAddressesSmtpFiltered.Add(proxyAddress.Substring(@"SMTP:".Length));
                        }
                    }
                }

                this.ProxyAddressesSMTPFiltered = new List<string>(proxyAddressesSmtpFiltered);
            }
            #endregion
        } 
        #endregion
    }
}
